/*
 *	Definitions for the 'struct sk_buff' memory handlers.
 *
 *	Authors:
 *		Alan Cox, <gw4pts@gw4pts.ampr.org>
 *		Florian La Roche, <rzsfl@rz.uni-sb.de>
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License
 *	as published by the Free Software Foundation; either version
 *	2 of the License, or (at your option) any later version.
 */

#ifndef _LINUX_SKBUFF_H
#define _LINUX_SKBUFF_H
#include <linux/malloc.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/config.h>

#undef CONFIG_SKB_CHECK

#define HAVE_ALLOC_SKB		/* For the drivers to know */

/*
用于表示释放某个数据包时该数据包时隶属于写缓冲区还是读缓冲区。对于每个套接字内核
分配一定的读写缓冲区，当缓冲区不够时，会发生数据包丢弃行为。所以及时维护缓冲区的
大小信息是至关重要的。而释放一个数据包时就是在得到更多的空闲空间以供其它数据包使
用，而读写缓冲区是不同的，所以释放时需要指示释放的这部分空间是属于哪个缓冲区。
*/
#define FREE_READ	1		// 释放的空间被加到读缓冲区中,即现在有更多的读空间
#define FREE_WRITE	0

/*数据包队列的头。注意其字段与 sk_buff 结构的开始几个字段时一样
的，如此处理可以利用指针转化将 sk_buff_head 当作 sk_buff 结构使用，反过来也如此*/
struct sk_buff_head {
  struct sk_buff		* volatile next;
  struct sk_buff		* volatile prev;
#if CONFIG_SKB_CHECK
  int				magic_debug_cookie;
#endif
};

/** 
 * sk_buff结构被用来封装网络数据。网络栈代码对数据的处理都是以 sk_buff 结构为单元进行的 
 * 1)接收数据：当网络设备驱动从网卡接收到数据包后，会创建一个sk_buff结构并填充数据。
 * 接下来，数据包通过不同的协议层（例如从链路层到网络层、传输层）逐层向上解析，每一层会依据sk_buff中的信息获取和解析相应的头部。
 * 2)发送数据：在数据发送时，数据包会逐层向下传递（从传输层到网络层、链路层），每层在需要时向sk_buff的头部添加协议头部，最终通过网络设备发送出去。
*/
struct sk_buff {
  struct sk_buff		* volatile next;
  struct sk_buff		* volatile prev;
#if CONFIG_SKB_CHECK
  int				magic_debug_cookie;
#endif
/*重发队列是由 sock 结构的 send_head, send_tail 字段指向的队列。send_head 指向这个队
列的首部，send_tail 指向这个队列的尾部。而队列的连接是使用 sk_buff 结构的 link3 字段完
成的。*/
  struct sk_buff		* volatile link3;	// 重发队列
  struct sock			*sk;	// 该数据包所属的套接字
  volatile unsigned long	when;	/* used to compute rtt's 该数据包的发送时间。该字段用于计算往返时间 RTT */
  struct timeval		stamp;
  struct device			*dev; /* 对于一个接收的数据包而言，该字段表示接收该数据包的接口设备。对于一个待发送的数据
包而言，该字段如果不为 NULL，则表示将要发送该数据包的接口设备。 */
  struct sk_buff		*mem_addr;	// 该 sk_buff 结构在内存中的基地址，该字段用于释放该 sk_buff 结构。
  union {
	struct tcphdr	*th;
	struct ethhdr	*eth;
	struct iphdr	*iph;
	struct udphdr	*uh;
	unsigned char	*raw;
	// seq 是针对使用 TCP 协议的待发送数据包而言，此时该字段值表示该数据包的ACK 值。ACK 值等于数据包中第一个数据的序列号加上数据的长度值。
	unsigned long	seq;	
  } h;	// 表示数据包在不同处理层次上所到达的处理位置。
  // 指向 IP 首部的指针，此处特别的分出一个字段用于指向 IP 首部主要用于 RAW 套接字。
  struct iphdr		*ip_hdr;		/* For IPPROTO_RAW */
  unsigned long			mem_len;	// sk_buff 结构大小加上数据帧的总长度
  unsigned long 		len;		// 只表示数据帧长度。即 len=mem_len – sizeof(sk_buff).
  unsigned long			fraglen;	// 示分片数据包个数
  struct sk_buff		*fraglist;	/* Fragment list 指向分片数据包队列v*/
  unsigned long			truesize;	// 同mem_len
  unsigned long 		saddr;		// 数据包发送的源端 IP 地址。
  unsigned long 		daddr;		// 数据包最终目的端 IP 地址。
  unsigned long			raddr;		/* next hop addr 数据包下一站 IP 地址 */
  volatile char 		acked,		// acked=1 表示该数据包已得到确认，可以从重发队列中删除。
				used,		// used=1 表示该数据包的数据已被应用程序读完，可以进行释放
				/*free=1 用于数据包发送，当某个待发送数据包 free 标志位等于 1，则表示无论该数据包是否
				发送成功，在进行发送操作后立即释放，无需缓存*/
				free,
				/*arp 用于待发送数据包，该字段等于 1 表示此待发送数据包已完成 MAC 首部的建立。arp=0
				表示 MAC 首部中目的端硬件地址尚不知晓，故需使用 ARP 协议询问对方，在 MAC 首部尚
				未完全建立之前，该数据包一直处于发送缓冲队列中（device 结构中 buffs 数组元素指向的
				某个队列以及 ARP 协议的某个队列中）。*/
				arp;
  /*tries 字段表示该数据包已进行 tries 试发送，如果试发送超出域值，则会放弃该数据包的发
	送。如对于 TCP 建立连接之 SYN 数据包，发送次数超过 3 次即放弃发送。
	lock 表示该数据包是否正在被系统其它部分使用。
	localroute 表示进行路由时是使用局域网路由（localroute=1）还是广域网路由。
	pkt_type表示数据包类型，比如 PACKET_HOST 发往本机的包*/				
  unsigned char			tries,lock,localroute,pkt_type;
#define PACKET_HOST		0		/* To us */
#define PACKET_BROADCAST	1
#define PACKET_MULTICAST	2
#define PACKET_OTHERHOST	3		/* Unmatched promiscuous */
  unsigned short		users;		/* User count - see datagram.c (and soon seqpacket.c/stream.c) 使用该数据包的模块数 */
  unsigned short		pkt_class;	/* For drivers that need to cache the packet type with the skbuff (new PPP) 意义同 pkt_type */
#ifdef CONFIG_SLAVE_BALANCING
  unsigned short		in_dev_queue; // 表示该数据包是否正在缓存于设备缓存队列中
#endif  
  unsigned long			padding[0];	// 填充字节。目前定义为 0 字节，即无填充
  unsigned char			data[0];	// 指向数据部分。数据一般紧接着该 sk_buff 结构，也有可能在任何地址处。
};
// 了套接字读写缓冲区的最大空间大小
#define SK_WMEM_MAX	32767
#define SK_RMEM_MAX	32767
// 用于调试目的。用于 magic_debug_cookie 字段的取值。
#ifdef CONFIG_SKB_CHECK
#define SK_FREED_SKB	0x0DE2C0DE
#define SK_GOOD_SKB	0xDEC0DED1
#define SK_HEAD_SKB	0x12231298
#endif

#ifdef __KERNEL__
/*
 *	Handling routines are only of interest to the kernel
 */

#include <asm/system.h>

#if 0
extern void			print_skb(struct sk_buff *);
#endif
extern void			kfree_skb(struct sk_buff *skb, int rw);
extern void			skb_queue_head_init(struct sk_buff_head *list);
extern void			skb_queue_head(struct sk_buff_head *list,struct sk_buff *buf);
extern void			skb_queue_tail(struct sk_buff_head *list,struct sk_buff *buf);
extern struct sk_buff *		skb_dequeue(struct sk_buff_head *list);
extern void 			skb_insert(struct sk_buff *old,struct sk_buff *newsk);
extern void			skb_append(struct sk_buff *old,struct sk_buff *newsk);
extern void			skb_unlink(struct sk_buff *buf);
extern struct sk_buff *		skb_peek_copy(struct sk_buff_head *list);
extern struct sk_buff *		alloc_skb(unsigned int size, int priority);
extern void			kfree_skbmem(struct sk_buff *skb, unsigned size);
extern struct sk_buff *		skb_clone(struct sk_buff *skb, int priority);
extern void			skb_device_lock(struct sk_buff *skb);
extern void			skb_device_unlock(struct sk_buff *skb);
extern void			dev_kfree_skb(struct sk_buff *skb, int mode);
extern int			skb_device_locked(struct sk_buff *skb);
/*
 *	Peek an sk_buff. Unlike most other operations you _MUST_
 *	be careful with this one. A peek leaves the buffer on the
 *	list and someone else may run off with it. For an interrupt
 *	type system cli() peek the buffer copy the data and sti();
 */
//  从list 指向的队列首部取下一个数据包，但该数据包并不从队列中删除。
static __inline__ struct sk_buff *skb_peek(struct sk_buff_head *list_)
{
	struct sk_buff *list = (struct sk_buff *)list_;
	return (list->next != list)? list->next : NULL;
}

#if CONFIG_SKB_CHECK
extern int 			skb_check(struct sk_buff *skb,int,int, char *);
#define IS_SKB(skb)		skb_check((skb), 0, __LINE__,__FILE__)
#define IS_SKB_HEAD(skb)	skb_check((skb), 1, __LINE__,__FILE__)
#else
#define IS_SKB(skb)		
#define IS_SKB_HEAD(skb)	
// 对一个 sk_buff_head 结构进行初始化操作
extern __inline__ void skb_queue_head_init(struct sk_buff_head *list)
{
	list->prev = (struct sk_buff *)list;
	list->next = (struct sk_buff *)list;
}

/*
 *	Insert an sk_buff at the start of a list.
 */
// 将 newsk 指向的数据包插入到 list 队列中的首部
extern __inline__ void skb_queue_head(struct sk_buff_head *list_,struct sk_buff *newsk)
{
	unsigned long flags;
	struct sk_buff *list = (struct sk_buff *)list_;

	save_flags(flags);
	cli();
	newsk->next = list->next;
	newsk->prev = list;
	newsk->next->prev = newsk;
	newsk->prev->next = newsk;
	restore_flags(flags);
}

/*
 *	Insert an sk_buff at the end of a list.
 */
// 将 newsk 指向的数据包插入到 list 队列的尾部
extern __inline__ void skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
{
	unsigned long flags;
	struct sk_buff *list = (struct sk_buff *)list_;

	save_flags(flags);
	cli();

	newsk->next = list;
	newsk->prev = list->prev;

	newsk->next->prev = newsk;
	newsk->prev->next = newsk;

	restore_flags(flags);
}

/*
 *	Remove an sk_buff from a list. This routine is also interrupt safe
 *	so you can grab read and free buffers as another process adds them.
 */
// 从 list 所指向队列首部取下一个数据包，并将该数据包从此队列中删除
extern __inline__ struct sk_buff *skb_dequeue(struct sk_buff_head *list_)
{
	long flags;
	struct sk_buff *result;
	struct sk_buff *list = (struct sk_buff *)list_;

	save_flags(flags);
	cli();

	result = list->next;
	if (result == list) {
		restore_flags(flags);
		return NULL;
	}

	result->next->prev = list;
	list->next = result->next;

	result->next = NULL;
	result->prev = NULL;

	restore_flags(flags);

	return result;
}

/*
 *	Insert a packet before another one in a list.
 */
// 将 newsk 指向的数据包插入到 old 指向的数据包的前面
extern __inline__ void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
{
	unsigned long flags;

	save_flags(flags);
	cli();
	newsk->next = old;
	newsk->prev = old->prev;
	old->prev = newsk;
	newsk->prev->next = newsk;

	restore_flags(flags);
}

/*
 *	Place a packet after a given packet in a list.
 */
// 将 newsk 指向的数据包插入到 old 指向的数据包的后面
extern __inline__ void skb_append(struct sk_buff *old, struct sk_buff *newsk)
{
	unsigned long flags;

	save_flags(flags);
	cli();

	newsk->prev = old;
	newsk->next = old->next;
	newsk->next->prev = newsk;
	old->next = newsk;

	restore_flags(flags);
}

/*
 *	Remove an sk_buff from its list. Works even without knowing the list it
 *	is sitting on, which can be handy at times. It also means that THE LIST
 *	MUST EXIST when you unlink. Thus a list must have its contents unlinked
 *	_FIRST_.
 */
// 将 skb 结构从其链入的队列中删除
extern __inline__ void skb_unlink(struct sk_buff *skb)
{
	unsigned long flags;

	save_flags(flags);
	cli();

	if(skb->prev && skb->next)
	{
		skb->next->prev = skb->prev;
		skb->prev->next = skb->next;
		skb->next = NULL;
		skb->prev = NULL;
	}
	restore_flags(flags);
}

#endif

extern struct sk_buff *		skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
extern int			datagram_select(struct sock *sk, int sel_type, select_table *wait);
extern void			skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
extern void			skb_free_datagram(struct sk_buff *skb);

#endif	/* __KERNEL__ */
#endif	/* _LINUX_SKBUFF_H */
